home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume4 / printfck < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  14.1 KB

  1. Subject: printfck -- makes lint check (most) printf calls
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 4, Issue 114
  6. Submitted by: Guido van Rossum <seismo!mcvax!guido>
  7.  
  8. Here's something a colleague of mind wrote one or two years ago, and
  9. which recently prompted some interest on the net.  Unfortunately it is
  10. not a very finished product; I post this so that others can benefit from
  11. it and change it to fit their needs.  I tried to compile and run it (on
  12. a VAX running 4.2BSD) and it gave sensible output when fed with itself
  13. as input -- I can't say more about the quality.  Foreseen use is
  14. something like:
  15.     printfck file.c >temp.c
  16.     lint temp.c procent.c
  17. Lint warnings about improper usage of any of the procent_* functions
  18. mean you're using an incorrect argument to a % escape.  For variable
  19. strings used as formats it doesn't help you; see also the comments at
  20. the begin of the program.
  21. Look in the program to find the command line options (you can feed it
  22. your own list of printf-like functions).
  23.  
  24. I'm sorry I can't spend more time on this (well I can but don't intend
  25. to since I have no need for it right now).  If anybody comes up with a
  26. manual, an improved version or any other changes, I'd like to hear about
  27. it.
  28.  
  29. Greetings,
  30.     Guido van Rossum, CWI, Amsterdam <guido@mcvax.UUCP>
  31.  
  32. #! /bin/sh
  33. # This is a shell archive, meaning:
  34. # 1. Remove everything above the #! /bin/sh line.
  35. # 2. Save the resulting text in a file.
  36. # 3. Execute the file with /bin/sh (not csh) to create the files:
  37. #    printfck.c
  38. #    procent.c
  39. #    procent.h
  40. # This archive created: Sun May 11 10:14:38 1986
  41. export PATH; PATH=/bin:$PATH
  42. echo shar: extracting "'printfck.c'" '(10441 characters)'
  43. if test -f 'printfck.c'
  44. then
  45.     echo shar: will not over-write existing file "'printfck.c'"
  46. else
  47. cat << \SHAR_EOF > 'printfck.c'
  48. /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/
  49. /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/
  50. /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */
  51. #include    <stdio.h>
  52.  
  53. /* Feed with a list of routine names and descriptions:
  54.  *    printf("",...)
  55.  *    sprintf(s,"",...)
  56.  *    fprintf(f,"",...)
  57.  * and with a source file; produce output in which occurrences of e.g.
  58.  *    sprintf(buf, "%s%ld", s, l)
  59.  * are replaced by
  60.  *    sprintf(buf, "%s%ld", procent_s(s), procent_L(l))
  61.  * Now let lint do the checking.
  62.  * Bugs:
  63.  *    Cases where the format string is not explicitly given (e.g., is the
  64.  *    result of some other routine, or looks like  bool ? "s1" : "s2")
  65.  *    are not handled.
  66.  *    Cases where the preprocessor produces quotes or comment delimiters
  67.  *    or concatenates partial identifiers are not handled.
  68.  *    We do not distinguish two sets of identifiers.
  69.  *    Only the parts lint sees get checked - not parts between (false)
  70.  *    #ifdef's. If the call to printf is outside #ifdef's, but some
  71.  *    args are inside, printfck may get confused. However, this is easy
  72.  *    to avoid:
  73.  *
  74.  *    THIS FAILS            THIS WORKS
  75.  *    ----------            ----------
  76.  *        printf("%s%d",            printf("%s%d", (
  77.  *    #ifdef debug            #ifdef debug
  78.  *            "foo"                "foo"
  79.  *    #else                #else
  80.  *            "bar"                "bar"
  81.  *    #endif debug            #endif debug
  82.  *            , num);                ), num);
  83.  *
  84.  */
  85.  
  86. char *index();
  87. char *rindex();
  88. char *malloc();
  89.  
  90. #define MAXIRS 100
  91.  
  92. struct ir {
  93.     char *rname;
  94.     int pn;        /* number of args preceding format string */
  95. } irs[MAXIRS+1] = {    /* should be read in - for now explicit */
  96.     "printf",    0,
  97.     "fprintf",    1,
  98.     "sprintf",    1,
  99. };
  100.  
  101. int nirs;
  102.  
  103. char *progname = "printfck";
  104. char *filename = NULL;
  105. FILE *inp;
  106.  
  107. int eof;
  108. int peekc;
  109. int lastc;
  110. int linenr;
  111.  
  112. initgetcx()
  113. {
  114.     eof = 0;
  115.     peekc = '\n';    /* recognize # on very first line */
  116.     lastc = 0;    /* result of last getchar() */
  117.     linenr = 1;
  118. }
  119.  
  120. getcx()
  121. {
  122.     register int c;
  123.  
  124.     if(peekc) {
  125.         c = peekc;
  126.         peekc = 0;
  127.     } else if(eof) {
  128.         c = EOF;
  129.     } else {
  130.         if(lastc) {
  131.             putchar(lastc);
  132.             lastc = 0;
  133.         }
  134.         if((c = getc(inp)) == EOF)
  135.             eof++;
  136.         else {
  137.             lastc = c;
  138.             if(c == '\n')
  139.                 linenr++;
  140.         }
  141.     }
  142.  
  143.     return(c);
  144. }
  145.  
  146. /* Note: we do not want to eliminate comments; perhaps they contain
  147.    lint directives. */
  148. getcy()        /* as getcx(), but skip comments */
  149. {
  150.     register int c = getcx();
  151.  
  152.     if(c == '/') {
  153.         c = getcx();
  154.         if(c == '*') {
  155.             while(1) {
  156.                 c = getcx();
  157.                 if(c == EOF)
  158.                     error("unfinished comment");
  159.                 while(c == '*') {
  160.                     c = getcx();
  161.                     if(c == '/')
  162.                         return(getcy());
  163.                 }
  164.             }
  165.         } else {
  166.             peekc = c;
  167.             c = '/';
  168.         }
  169.     }
  170.     return(c);
  171. }
  172.  
  173. getcz()        /* as getcy(), but skip preprocessor directives */
  174. {
  175.     register int c = getcy();
  176.  
  177.     while(c == '\n') {
  178.         c = getcx();
  179.         if(c == '#') {
  180.             while(c != '\n') {
  181.                 c = getcx();
  182.                 if(c == EOF)
  183.                     error("incomplete line");
  184.                 while(c == '\\') {
  185.                     (void) getcx(); c = getcx();
  186.                 }
  187.             }
  188.         } else {
  189.             peekc = c;
  190.             return('\n');
  191.         }
  192.     }
  193.     return(c);
  194. }
  195.  
  196. getcq()        /* as getcz() but skip strings */
  197. {
  198.     register int c = getcz();
  199.     register int delim;
  200.  
  201.     if(c == '\'' || c == '"') {
  202.         delim = c;
  203.         while(1) {
  204.             c = getcx();
  205.             if(c == '\n' || c == EOF)
  206.                 error("Unfinished string (delim %c)", delim);
  207.             if(c == '\\') {
  208.                 (void) getcx();
  209.                 continue;
  210.             }
  211.             if(c == delim)
  212.                 return(getcq());
  213.         }
  214.     }
  215.     return(c);
  216. }
  217.  
  218. usage()
  219. {
  220.     fprintf(stderr,
  221.       "Usage: %s [-n] [-e function] ... [-f datafile] ... [file.c] ...\n",
  222.       progname);
  223.     exit(2);
  224. }
  225.  
  226. extern char *optarg;
  227. extern int optind;
  228.  
  229. main(argc, argv)
  230. int argc;
  231. char **argv;
  232. {
  233.     register int c;
  234.     FILE *fp;
  235.  
  236.     if (argc > 0) {
  237.         progname = rindex(argv[0], '/');
  238.         if (progname != NULL)
  239.             ++progname;
  240.         else
  241.             progname = argv[0];
  242.     }
  243.  
  244.     for (; irs[nirs].rname != NULL; ) ++nirs; /* Count defaults */
  245.  
  246.     while ((c = getopt(argc, argv, "e:nf:")) != EOF) {
  247.         switch (c) {
  248.         case '?':
  249.             usage();
  250.             /* NOTREACHED */
  251.         case 'e':
  252.             addir(optarg, 0);
  253.             break;
  254.         case 'n':
  255.             nirs = 0;
  256.             irs[nirs].rname = NULL;
  257.             break;
  258.         case 'f':
  259.             getirfile(optarg);
  260.             break;
  261.         }
  262.     }
  263.  
  264.     printf("#include \"procent.h\"\n");
  265.  
  266.     if (optind == argc)
  267.         treat(stdin, NULL);
  268.     else {
  269.         for (; optind < argc; ++optind) {
  270.             if (strcmp(argv[optind], "-") == 0)
  271.                 treat(stdin, NULL);
  272.             else {
  273.                 filename = argv[optind];
  274.                 fp = fopen(filename, "r");
  275.                 if (fp == NULL)
  276.                     filerror(filename);
  277.                 treat(fp, filename);
  278.                 fclose(fp);
  279.             }
  280.         }
  281.     }
  282.     exit(0);
  283. }
  284.  
  285. treat(fp, file)
  286. FILE *fp;
  287. char *file;
  288. {
  289.     register int c;
  290.  
  291.     filename = file;
  292.     linenr = 0;
  293.     inp = fp;
  294.     initgetcx();
  295.     while((c = getcq()) != EOF) {
  296.  
  297.         /* check for (interesting) identifiers */
  298.         if(letter(c))
  299.             rd_id(c);
  300.     }
  301.     filename = NULL;
  302.     linenr = 0;
  303.     irs[nirs].rname = NULL;
  304. }
  305.  
  306.  
  307. rd_id(first)
  308. register int first;
  309. {
  310.     char idf[256];
  311.     register char *ip = idf;
  312.     register int c;
  313.  
  314.     *ip++ = first;
  315.     while(letdig(c = getcx()))
  316.         if(ip-idf < sizeof(idf)-1)
  317.             *ip++ = c;
  318.     peekc = c;
  319.     *ip = 0;
  320.     handle(idf);
  321. }
  322.  
  323. /*VARARGS1*/
  324. error(s, x)
  325. char *s;
  326. {
  327.     printf("\n"); /* Finish incomplete output line */
  328.     fprintf(stderr, "%s: Error (", progname);
  329.     if (filename != NULL) fprintf(stderr, "%s, ", filename);
  330.     fprintf(stderr, "line %d): ", linenr);
  331.     fprintf(stderr, s, x);
  332.     fprintf(stderr, "\n");
  333.     exit(1);
  334. }
  335.  
  336. /*VARARGS1*/
  337. warning(s, x1, x2)
  338. char *s;
  339. {
  340.     fprintf(stderr, "%s: Warning (", progname);
  341.     if (filename != NULL) fprintf(stderr, "%s, ", filename);
  342.     fprintf(stderr, "line %d): ", linenr);
  343.     fprintf(stderr, s, x1, x2);
  344.     fprintf(stderr, "\n");
  345. }
  346.  
  347. filerror(file)
  348. char *file;
  349. {
  350.     fprintf(stderr, "%s: can't open ", progname);
  351.     perror(file);
  352.     exit(2);
  353. }
  354.  
  355. letter(c)
  356. register int c;
  357. {
  358.     return(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_');
  359. }
  360.  
  361. digit(c)
  362. register int c;
  363. {
  364.     return('0' <= c && c <= '9');
  365. }
  366.  
  367. letdig(c)
  368. register int c;
  369. {
  370.     return(letter(c) || digit(c));
  371. }
  372.  
  373. handle(idf)
  374. register char *idf;
  375. {
  376.     register struct ir *irp = irs;
  377.  
  378.     while(irp->rname) {
  379.         if(!strcmp(idf, irp->rname)) {
  380.             doit(irp);
  381.             return;
  382.         }
  383.         irp++;
  384.     }
  385. }
  386.  
  387. skipspaces()
  388. {
  389.     register int c;
  390.  
  391.     while(1) {
  392.         c = getcz();
  393.         if(c == ' ' || c == '\t' || c == '\n')
  394.             continue;
  395.         peekc = c;
  396.         return;
  397.     }
  398. }
  399.  
  400. doit(irp)
  401. register struct ir *irp;
  402. {
  403.     register int c, cnt = irp->pn;
  404.  
  405.     skipspaces();
  406.     if((c = getcz()) != '(') {
  407.         peekc = c;
  408.         warning("%s not followed by '('", irp->rname);
  409.         return;
  410.     }
  411.  
  412.     while(cnt--) {
  413.         c = skiparg();
  414.         if(c != ',') {
  415.             peekc = c;
  416.             warning("arg of %s not followed by comma", irp->rname);
  417.             return;
  418.         }
  419.     }
  420.     skipspaces();
  421.  
  422.     /* now parse format string (if present) */
  423.     /* (here we also avoid defining occurrences) */
  424.     if((c = getcx()) != '"') {
  425.         peekc = c;
  426.         return;
  427.     }
  428.     domore(irp);
  429. }
  430.  
  431. domore(irp)
  432. register struct ir *irp;
  433. {
  434.     char fmt[256];
  435.     register char *fp = fmt;
  436.     register int c;
  437.  
  438.     while(1) {
  439.         c = getcx();
  440.         if(c == '\n' || c == EOF)
  441.             error("Unfinished format string");
  442.         if(c == '"')
  443.             break;
  444.         if(c != '%') {
  445.             if (c == '\\')
  446.                 (void) getcx();
  447.             continue;
  448.         }
  449.         c = getcx();
  450.         if(c == '%')
  451.             continue;
  452.         if(c == '-')
  453.             c = getcx();
  454.         if(c == '*') {
  455.             c = getcx();
  456.             if(fp-fmt < sizeof(fmt)-1)
  457.                 *fp++ = 'd';
  458.         } else while(digit(c))
  459.             c = getcx();
  460.         if(c == '.')
  461.             c = getcx();
  462.         if(c == '*') {
  463.             c = getcx();
  464.             if(fp-fmt < sizeof(fmt)-1)
  465.                 *fp++ = 'd';
  466.         } else while(digit(c))
  467.             c = getcx();
  468.         if(c == '#')
  469.             c = getcx();
  470.         if(c == 'l') {
  471.             c = getcx();
  472.             if('a' <= c && c <= 'z')
  473.                 c -= 'a'-'A';
  474.             else
  475.                 error("%%l not followed by lowercase");
  476.         }
  477.         if(fp-fmt < sizeof(fmt)-1)
  478.             *fp++ = c;
  479.         else
  480.             warning("ridiculously long format");
  481.     }
  482.     *fp = 0;
  483.     fp = fmt;
  484.     skipspaces();
  485.     while((c = getcz()) == ',') {
  486.         if(!*fp)
  487.             error("%s has too many arguments", irp->rname);
  488.         skipspaces();
  489.         printf("procent_%c(", *fp++);
  490.         c = skiparg();
  491.         printf(")");
  492.         peekc = c;
  493.     }
  494.     if(c != ')')
  495.         error("%s has ill-formed argument list", irp->rname);
  496.     if(*fp)
  497.         error("%s has too few arguments", irp->rname);
  498. }
  499.  
  500. skiparg()
  501. {
  502.     register int parenct = 0;
  503.     register int c;
  504.  
  505.     parenct = 0;
  506.     while(1) {
  507.         c = getcq();
  508.         if(c == EOF)
  509.             error("eof in arg list");
  510.         if(!parenct && (c == ',' || c == ')'))
  511.             return(c);
  512.         if(c == '(' || c == '[') {
  513.             parenct++;
  514.             continue;
  515.         }
  516.         if(c == ')' || c == ']') {
  517.             parenct--;
  518.             continue;
  519.         }
  520.     }
  521. }
  522.  
  523. char *strsave(s)
  524. char *s;
  525. {
  526.     char *t = malloc(strlen(s) + 1);
  527.     if (t == NULL) error("out of memory");
  528.     strcpy(t, s);
  529.     return t;
  530. }
  531.  
  532. getirfile(irfile)
  533. char *irfile;
  534. {
  535.     FILE *fp = fopen(irfile, "r");
  536.     char line[256];
  537.     char name[256];
  538.     char *end;
  539.     int n;
  540.     int cnt;
  541.  
  542.     if (fp == NULL) filerror(irfile);
  543.     filename = irfile;
  544.     linenr = 0;
  545.     while (fgets(line, sizeof line, fp)) {
  546.         ++linenr;
  547.         end = index(line, '#');
  548.         if (end) *end = '\0';
  549.         n= sscanf(line, " %s %d %s", name, &cnt, name+1);
  550.         if (n == 0 || name[0] == '\0')
  551.             continue; /* Skip empty line or comment */
  552.         if (n != 2 || cnt < 0)
  553.             error("bad format (must be %%s %%u)");
  554.         /* Should also check for valid name... */
  555.         addir(strsave(name), cnt);
  556.     }
  557.     fclose(fp);
  558.     filename = NULL;
  559.     linenr = 0;
  560. }
  561.  
  562. addir(name, cnt)
  563. char *name;
  564. int cnt;
  565. {
  566.     if (nirs >= MAXIRS) error("table overflow");
  567.     irs[nirs].rname = name;
  568.     irs[nirs].pn = cnt;
  569.     ++nirs;
  570. }
  571.  
  572. /*
  573.  * get option letter from argument vector
  574.  */
  575. int    opterr = 1,        /* useless, never set or used */
  576.     optind = 1,        /* index into parent argv vector */
  577.     optopt;            /* character checked for validity */
  578. char    *optarg;        /* argument associated with option */
  579.  
  580. #define BADCH    (int)'?'
  581. #define EMSG    ""
  582. #define tell(s)    fputs(*nargv,stderr);fputs(s,stderr); \
  583.         fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
  584.  
  585. getopt(nargc,nargv,ostr)
  586. int    nargc;
  587. char    **nargv,
  588.     *ostr;
  589. {
  590.     static char    *place = EMSG;    /* option letter processing */
  591.     register char    *oli;        /* option letter list index */
  592.     char    *index();
  593.  
  594.     if(!*place) {            /* update scanning pointer */
  595.         if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
  596.         if (*place == '-') {    /* found "--" */
  597.             ++optind;
  598.             return(EOF);
  599.         }
  600.     }                /* option letter okay? */
  601.     if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
  602.         if(!*place) ++optind;
  603.         tell(": illegal option -- ");
  604.     }
  605.     if (*++oli != ':') {        /* don't need argument */
  606.         optarg = NULL;
  607.         if (!*place) ++optind;
  608.     }
  609.     else {                /* need an argument */
  610.         if (*place) optarg = place;    /* no white space */
  611.         else if (nargc <= ++optind) {    /* no arg */
  612.             place = EMSG;
  613.             tell(": option requires an argument -- ");
  614.         }
  615.          else optarg = nargv[optind];    /* white space */
  616.         place = EMSG;
  617.         ++optind;
  618.     }
  619.     return(optopt);            /* dump back option letter */
  620. }
  621.  
  622. SHAR_EOF
  623. if test 10441 -ne "`wc -c < 'printfck.c'`"
  624. then
  625.     echo shar: error transmitting "'printfck.c'" '(should have been 10441 characters)'
  626. fi
  627. fi
  628. echo shar: extracting "'procent.c'" '(848 characters)'
  629. if test -f 'procent.c'
  630. then
  631.     echo shar: will not over-write existing file "'procent.c'"
  632. else
  633. cat << \SHAR_EOF > 'procent.c'
  634. /*LINTLIBRARY*/
  635. int procent_d(x) int x; { return x; }
  636. int procent_o(x) int x; { return x; }
  637. int procent_x(x) int x; { return x; }
  638. long procent_D(x) long x; { return x; }
  639. long procent_O(x) long x; { return x; }
  640. long procent_X(x) long x; { return x; }
  641. double procent_e(x) double x; { return x; }
  642. double procent_f(x) double x; { return x; }
  643. double procent_g(x) double x; { return x; }
  644. double procent_E(x) double x; { return x; }
  645. double procent_F(x) double x; { return x; }
  646. double procent_G(x) double x; { return x; }
  647.  
  648. /* NOTE: not all C compilers support unsigned long! - If your compiler rejects
  649.  * the following line, replace "unsigned long" with just "long"
  650.  */
  651. unsigned long procent_U(x) unsigned long x; { return x; }
  652. unsigned procent_u(x) unsigned x; { return x; }
  653. int procent_c(x) int x; { return x; }
  654. char *procent_s(x) char *x; { return x; }
  655. SHAR_EOF
  656. if test 848 -ne "`wc -c < 'procent.c'`"
  657. then
  658.     echo shar: error transmitting "'procent.c'" '(should have been 848 characters)'
  659. fi
  660. fi
  661. echo shar: extracting "'procent.h'" '(542 characters)'
  662. if test -f 'procent.h'
  663. then
  664.     echo shar: will not over-write existing file "'procent.h'"
  665. else
  666. cat << \SHAR_EOF > 'procent.h'
  667. /* procent.h
  668.  * this file is the header for files being lint'ed using printfck
  669.  */
  670.  
  671. int procent_d();
  672. int procent_o();
  673. int procent_x();
  674. long procent_D();
  675. long procent_O();
  676. long procent_X();
  677. double procent_e();
  678. double procent_f();
  679. double procent_g();
  680. double procent_E();
  681. double procent_F();
  682. double procent_G();
  683.  
  684. /* NOTE: not all C compilers support unsigned long! - If your compiler rejects
  685.  * the following line, replace "unsigned long" with just "long"
  686.  */
  687. unsigned long procent_U();
  688. unsigned procent_u();
  689. int procent_c();
  690. char *procent_s();
  691. SHAR_EOF
  692. if test 542 -ne "`wc -c < 'procent.h'`"
  693. then
  694.     echo shar: error transmitting "'procent.h'" '(should have been 542 characters)'
  695. fi
  696. fi
  697. exit 0
  698. #    End of shell archive
  699.